<?php defined('SYSPATH') or die('No direct script access.');

/**
 * Service Driver Library
 *
 * $Id: Service.php 166 2008-12-10 11:03:06Z orbit.wacsg $
 *
 * @package		Service Module
 * @author		Alex Sancho
 * @copyright	(c) 2008 Alex Sancho
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 * 3. Neither the name of copyright holders nor the names of its
 *	  contributors may be used to endorse or promote products derived
 *	  from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

abstract class Service_Driver
{
	const TIMEOUT_CONNECTION = 20;
	const TIMEOUT_TOTAL = 50;

	private $credentials;
	private $http_status;
	private $http_headers = array();
	private $last_api_call;

	protected $post = false;
	protected $post_args = '';
	protected $put = false;
	protected $delete = false;

	protected $api_url = '';
	protected $port = 80;
	protected $user_agent = 'Kohana PHP 5 FrameWork';
	protected $charset = 'UTF-8';
	protected $use_cache = false;
	protected $cache_ttl = 1800;

	/**
	 * __construct
	 *
	 * @param array $config driver configuration params
	 * @return void
	 * @access public
	 *
	 */
	public function __construct($config = array())
	{
		foreach($config as $key => $val)
		{
			$method = 'set_'.$key;

			if (method_exists($this, $method))
				$this->$method($val);
		}
	}

	/**
	 * set_cache
	 *
	 * @param integer $ttl
	 * @return object
	 * @access public
	 *
	 */
	public function set_cache($ttl = 1800)
	{
		$this->use_cache = true;
		$this->cache_ttl = $ttl;
		return $this;
	}

	/**
	 * set_charset
	 *
	 * @param string $charset
	 * @return object
	 * @access public
	 *
	 */
	public function set_charset($charset)
	{
		$this->charset = $charset;
		return $this;
	}

	/**
	 * set_api_url
	 *
	 * @param string $api_url
	 * @return object
	 * @access public
	 *
	 */
	public function set_api_url($url)
	{
		if ( ! valid::url($url))
			throw new Kohana_User_Exception('Service Module', 'service.invalid_url');

		$this->api_url = $url;
		return $this;
	}

	/**
	 * set_port
	 *
	 * @param integer $port
	 * @return object
	 * @access public
	 *
	 */
	public function set_port($port)
	{
		if ( ! is_int($port))
			throw new Kohana_User_Exception('Service Module', 'service.invalid_port');

		$this->port = $port;
		return $this;
	}

	/**
	 * set_useragent
	 *
	 * @param string $agent
	 * @return object
	 * @access public
	 *
	 */
	public function set_useragent($agent)
	{
		if ( ! is_string($agent))
			throw new Kohana_User_Exception('Service Module', 'service.invalid_ua');

		$this->user_agent = $agent;
		return $this;
	}

	/**
	 * set_credentials
	 *
	 * @param array $credentials
	 * @return object
	 * @access public
	 *
	 */
	public function set_credentials( array $credentials)
	{
		if ( ! isset($credentials['username']) or ! isset($credentials['password']))
			throw new Kohana_User_Exception('Service Module', 'service.invalid_credentials');

		$this->credentials = sprintf("%s:%s", $credentials['username'], $credentials['password']);
		return $this;
	}

	/**
	 * last_http_headers
	 *
	 * @return array
	 * @access public
	 *
	 */
	public function last_http_headers()
	{
		return $this->http_headers;
	}

	/**
	 * last_status_code
	 *
	 * @return string
	 * @access public
	 *
	 */
	public function last_status_code()
	{
		return $this->http_status;
	}

	/**
	 * last_api_call
	 *
	 * @return string
	 * @access public
	 *
	 */
	public function last_api_call()
	{
		return $this->last_api_call;
	}

	/**
	 * post
	 *
	 * @param mixed $args
	 * @return object
	 * @access protected
	 *
	 */
	protected function post($args = '')
	{
		$this->post = true;
		$this->post_args = $args;

		return $this;
	}

	/**
	 * put
	 *
	 * @param mixed $args
	 * @return object
	 * @access protected
	 *
	 */
	protected function put($args = '')
	{
		$this->put = $args;

		return $this;
	}

	/**
	 * delete
	 *
	 * @return object
	 * @access protected
	 *
	 */
	protected function delete()
	{
		$this->delete = true;

		return $this;
	}

	/**
	 * header
	 *
	 * @return array
	 * @access public
	 *
	 */
	protected function header()
	{
		return array
		(
			"Cache-Control: max-age=0",
			"Connection: keep-alive",
			"Keep-Alive: 300",
			"Accept-Charset: ".strtolower($this->charset),
			"Pragma: "
		);
	}

	/**
	 * parse_headers
	 *
	 * @param object $curl
	 * @param string $data
	 * @return integer
	 * @access protected
	 *
	 */
	protected function parse_headers($curl, $data)
	{
		list($header, $value) = explode(': ', trim($data), 2);

		$this->http_headers[$header] = $value;

		return strlen($data);
	}


	/**
	 * api_url
	 *
	 * @param string $path
	 * @param string $separator
	 * @return string
	 * @access protected
	 *
	 */
	protected function api_url($path, $separator = '&')
	{
		return $this->api_url.$path;
	}

	/**
	 * api_call
	 *
	 * @param string $apiURL
	 * @param boolean $credentials
	 * @param integer $timeout
	 * @return mixed
	 * @access protected
	 *
	 */
	protected function api_call($api_url, $credentials = false, $timeout = self::TIMEOUT_TOTAL)
	{
		$api_url = $this->api_url($api_url);

		if ( ! $this->use_cache or ($this->use_cache and ! $data = Cache::instance()->get('service-'.get_class($this).'-'.md5($api_url))))
		{
			$handle = curl_init();

			curl_setopt_array($handle, array
			(
				CURLOPT_URL				=> $api_url,
				CURLOPT_PORT			=> $this->port,
				CURLOPT_USERAGENT		=> $this->user_agent,
				CURLOPT_HTTPHEADER		=> $this->header(),
				CURLOPT_HEADERFUNCTION	=> array($this, 'parse_headers'),
				CURLOPT_FAILONERROR		=> true,
				CURLOPT_FOLLOWLOCATION	=> true,
				CURLOPT_RETURNTRANSFER	=> true,
				CURLOPT_CONNECTTIMEOUT	=> self::TIMEOUT_CONNECTION,
				CURLOPT_TIMEOUT			=> $timeout
			));

			if ($credentials === true)
				curl_setopt($handle, CURLOPT_USERPWD, $this->credentials);

			if ($this->post === true)
				curl_setopt($handle, CURLOPT_POST, true);

			if (is_string($this->post_args) or count($this->post_args) > 0)
				curl_setopt($handle, CURLOPT_POSTFIELDS, $this->post_args);

			if ($this->put !== false)
			{
				curl_setopt_array($handle, array
				(
					CURLOPT_PUT			=> true,
					CURLOPT_INFILE		=> $this->put,
					CURLOPT_INFILESIZE	=> strlen($this->put)
				));
			}

			if ($this->delete === true)
				curl_setopt($handle, CURLOPT_CUSTOMREQUEST, 'DELETE');

			if (strpos($api_url, 'https://') !== false)
				curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);

			$data = curl_exec($handle);
			$this->http_status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
//			$headers = curl_getinfo($handle);
//			$this->http_headers = array_merge($headers, $this->http_headers);
			$this->last_api_call = $api_url;
			curl_close($handle);

			if ($this->use_cache === true)
				Cache::instance()->set('service-'.get_class($this).'-'.md5($api_url), $data, null, $this->cache_ttl);
		}

		return $data;
	}

	/**
	 * implode
	 *
	 * @param mixed $args
	 * @param string $separator
	 * @return string
	 * @access protected
	 *
	 */
	protected static function implode($args, $separator = ",")
	{
		if (is_array($args))
			$args = implode($separator, $args);

		return $args;
	}

} //End Service Driver
